/****************************************************************
 * I am the AC 130 vs Pirate game. I allow the user to control an 
 * AC130 gunship in the hunt for Modern day pirates. The player  
 * will be allowed to roam free across a section of ocean/coastline.  
 * In the water there will be four types of vessels: Merchant  
 * ships that the player must protect, Pirate ships that the player  
 * must identify and destroy, Fishing boats that look like Pirate  
 * ships that the player must not destroy, and Friendly Navy ships  
 * that can capture pirates for extra points. Time permitting there  
 * may also be ground targets to attack. 
 *
 * Key elements of this project will be the Player/AC130, the Ships,  
 * the ShipAI system, the collision grid, the debris controller, the  
 * Ground/Water, Clouds, and scoring system. This project will make  
 * use of an external text file to store useful game parameters,  
 * thus allowing us to play balance the game without recompiling  
 * the executable. 
 *****************************************************************
 * I am the Crash Bin class. I act as an inter object reference system. 
 * Each object in the world is required to register its current position
 * with me. Once registered an index to the current object can be found
 * within a bounding area. Other object can then reference this bounding 
 * area to find the indexes of the registered objects. This method is 
 * preferable to the O(n*m) algorithem that has to be run to compare
 * all elements of one array to another.
 *****************************************************************/

#include "CrashBin.h"

/// constrctor sets default values
CrashBin::CrashBin(){
	// grid data
	gridWidth = 5;
	gridHeight = 5;
	nodeWidth = 5;
	nodeHeight = 5;
	gridRef = new int[gridWidth*gridHeight]; // references bin
	gridAge = new int[gridWidth*gridHeight];// age of index;
	curAge = 0;	// current age

	// bin data
	maxBin = 0;	// top used bin
	for (int itor =0; itor < BINMAX; itor++){
		for(int itor2 =0; itor2 < BINHGT; itor2++){
			binType[itor][itor2];
			binIndex[itor][itor2];
		}
		binTop[itor] = 0;
	}
}
CrashBin::~CrashBin(){
	delete(gridRef);
	delete(gridAge);
}

/// initilize dimentions from Game data
void CrashBin::Init(GameData * exGd){
	gd = exGd;
	nodeWidth = gd->GetNum("CbNodeWidth");
	nodeHeight = gd->GetNum("CbNodeHeight");
	if(nodeWidth < 1)nodeWidth = 64;
	if(nodeHeight < 1)nodeHeight = 64;
}

// sets the gridRef and gridAge to appropriare dimentions
void CrashBin::SetLevel(int width, int height){
		// delete old data arrays
	delete(gridRef);
	delete(gridAge);
		//set new grid dimentions
	gridWidth = width/nodeWidth;
	gridHeight = height/nodeHeight;
		// initilize new data arrays
	gridRef = new int[gridWidth*gridHeight]; 
	gridAge = new int[gridWidth*gridHeight];
		// initilize all gridref points to -1
	for(int itor = 0; itor < gridWidth*gridHeight; itor++){
		gridRef[itor] = gridAge[itor] = -1;
	}
}

// called at te start of the register process;
// to empty old registry;
void CrashBin::ClearRegister(){
	curAge += 1;	// incriment per cycle so that all of the 
					// old grid ages are flagged as old
	if( curAge > 2048) curAge = 0;	// overflow
		// prepair for insert
	maxBin = 0;
	for (int itor =0; itor < BINMAX; itor++){
		binTop[itor] = 0;
	}
}

// register an individual element
void CrashBin::Register(tpBinType type, int index, float xval, float yval){
	int gridLoc = GetIndex(xval, yval);
	if( gridAge[gridLoc] != curAge){	// entering a new bin
			// store reference
		gridAge[gridLoc] = curAge;
		gridRef[gridLoc] = maxBin;
		binType[gridRef[gridLoc]][binTop[gridRef[gridLoc]]] = type;
		binIndex[gridRef[gridLoc]][binTop[gridRef[gridLoc]]] = index;
			// incriment tops
		if( maxBin < BINMAX-1) maxBin += 1;
		if(binTop[gridRef[gridLoc]] < BINHGT-1)
			binTop[gridRef[gridLoc]] +=1;
	}else{	// enter current bin
		binType[gridRef[gridLoc]][binTop[gridRef[gridLoc]]] = type;
		binIndex[gridRef[gridLoc]][binTop[gridRef[gridLoc]]] = index;
			// incriment tops
		if(binTop[gridRef[gridLoc]] < BINHGT-1)
			binTop[gridRef[gridLoc]] +=1;
	}
}

/// Count the registered elements in the specified area
/// this value can be used as a max index 
int CrashBin::CountArea(float xval, float yval, float radius){
		// set iteration limits
	int xStart =  xval - radius;
	int xEnd = xval+radius;
	int yStart = yval - radius;
	int yEnd = yval + radius;
		// enfouce bounds
	if( xStart < 0) xStart = 0;
	if( yStart < 0) yStart = 0;
	if( xEnd > gridWidth*nodeWidth-1) 
		xEnd = gridWidth*nodeWidth-1;
	if( yEnd > gridHeight*nodeHeight-1) 
		yEnd = gridHeight*nodeHeight-1;
		// return value
	int ret =0;
		// populate ret with binTop values from within the specified area
	for(int xitor = xStart; xitor < xEnd; xitor += nodeWidth){
		for(int yitor = yStart; yitor < yEnd; yitor += nodeHeight){
			int gridLoc = GetIndex(xitor, yitor);
				// check index validity
			if(curAge == gridAge[gridLoc] && gridRef[gridLoc] > 0){
				int binRef = gridRef[gridLoc];
				ret += binTop[binRef];	// add this bin's max to total
			}
		}
	}

	return ret;
}

/// I pull the indexed type value from this object. The index references 
/// to the n'th element inside the radius arround xval, yval. The number
/// of elements can be found via CountArea(). Itorating from 0 to CountArea()
/// with this function will retrieve every element in the specified area.
tpBinType CrashBin::FetchType(int index, float xval, float yval, float radius){
		// set iteration limits
	int xStart =  xval - radius;
	int xEnd = xval+radius;
	int yStart = yval - radius;
	int yEnd = yval + radius;
		// enfouce bounds
	if( xStart < 0) xStart = 0;
	if( yStart < 0) yStart = 0;
	if( xEnd > gridWidth*nodeWidth-1) 
		xEnd = gridWidth*nodeWidth-1;
	if( yEnd > gridHeight*nodeHeight-1) 
		yEnd = gridHeight*nodeHeight-1;
		// count of elements accessed value
	int ret =0;
		// populate ret with binTop values from within the specified area
	for(int xitor = xStart; xitor < xEnd; xitor += nodeWidth){
		for(int yitor = yStart; yitor < yEnd; yitor += nodeHeight){
			int gridLoc = GetIndex(xitor, yitor);
				// check index validity
			if(curAge == gridAge[gridLoc] && gridRef[gridLoc] > 0){
					// bin array indexer 1
				int binRef = gridRef[gridLoc];
					// check if this is the bin containing
					// the element we need.
				if(ret + binTop[binRef] > index){
						// bin array indexer 2 
					int found = index - ret;
					return binType[binRef][found];
				}else{
					ret += binTop[binRef];	// add this bin's max to total
				}
			}
		}
	}
		// failure to find
	return tpBinType::btNull;

}

/// Works just like FetchType only on the index value.
int CrashBin::FetchIndex(int index, float xval, float yval, float radius){
		// set iteration limits
	int xStart =  xval - radius;
	int xEnd = xval+radius;
	int yStart = yval - radius;
	int yEnd = yval + radius;
		// enfouce bounds
	if( xStart < 0) xStart = 0;
	if( yStart < 0) yStart = 0;
	if( xEnd > gridWidth*nodeWidth-1) 
		xEnd = gridWidth*nodeWidth-1;
	if( yEnd > gridHeight*nodeHeight-1) 
		yEnd = gridHeight*nodeHeight-1;
		// count of elements accessed value
	int ret =0;
		// populate ret with binTop values from within the specified area
	for(int xitor = xStart; xitor < xEnd; xitor += nodeWidth){
		for(int yitor = yStart; yitor < yEnd; yitor += nodeHeight){
			int gridLoc = GetIndex(xitor, yitor);
				// check index validity
			if(curAge == gridAge[gridLoc] && gridRef[gridLoc] > 0){
					// bin array indexer 1
				int binRef = gridRef[gridLoc];
					// check if this is the bin containing
					// the element we need.
				if(ret + binTop[binRef] > index){
						// bin array indexer 2 
					int found = index - ret;
					return binIndex[binRef][found];
				}else{
					ret += binTop[binRef];	// add this bin's max to total
				}
			}
		}
	}
		// failure to find
	return tpBinType::btNull;
}

// conversion function to translate a posiiton to 
int CrashBin::GetIndex(float x, float y){
		// convert to grid index
	int xloc = x/nodeWidth;
	int yloc = y/nodeHeight;
		// ensure within bounds
	if(xloc < 0)xloc=0;
	if(yloc < 0)yloc=0;
	if(xloc > gridWidth-1) xloc=gridWidth-1;
	if(yloc > gridHeight-1) yloc=gridHeight-1;
		// return index
	return xloc+yloc*gridWidth;
}
